import thostmduserapi as mdapi
from datetime import time
import pandas as pd

'''需要订阅的合约list'''
subID = ["IC2012"]

class CBarData:
    def _init__(self):
        self.instrumentID: str
        self.updateTime: time
        self.volume: float = 0
        self.openInterest: float = 0
        self.openPrice: float = 0
        self.highPrice: float = 0
        self.lowPrice: float = 0
        self.closePrice: float = 0

# 继承自spi基类mdapi.CThostFtdcMdSpi
class CFtdcMdSpi(mdapi.CThostFtdcMdSpi):

    def __init__(self, tapi):
        mdapi.CThostFtdcMdSpi.__init__(self)
        self.tapi = tapi
        self.bar = None
        self.lastVolume: double = 0
        self.submap = {}

    def OnFrontConnected(self) -> "void":
        print("OnFrontConnected")
        loginfield = mdapi.CThostFtdcReqUserLoginField()
        loginfield.BrokerID = "9999"
        loginfield.UserID = "171525"
        loginfield.Password = "1234qwer@"
        loginfield.UserProductInfo = "python dll"
        # 实现onfrontconnect函数，在里面调用登录，第7步
        self.tapi.ReqUserLogin(loginfield, 0)

    def OnRspUserLogin(self, pRspUserLogin: 'CThostFtdcRspUserLoginField', pRspInfo: 'CThostFtdcRspInfoField',
                       nRequestID: 'int', bIsLast: 'bool') -> "void":
        print(
            f"OnRspUserLogin, SessionID={pRspUserLogin.SessionID},ErrorID={pRspInfo.ErrorID},ErrorMsg={pRspInfo.ErrorMsg}")
        # 继承实现登录回调，登录成功后去订阅，第9步
        ret = self.tapi.SubscribeMarketData([id.encode('utf-8') for id in subID], len(subID))

    def OnRtnDepthMarketData(self, pDepthMarketData: 'CThostFtdcDepthMarketDataField') -> "void":
        # 继承收取订阅行情，第11步,在这里将pDepthMarketData数据存入csv即可录得数据
        print(f"InstrumentID={pDepthMarketData.InstrumentID},LastPrice={pDepthMarketData.LastPrice}")
        # 根据行情中的UpdateTime字段判断是否为新1分钟
        st = pDepthMarketData.UpdateTime.split(':')
        if not self.bar:
            newMinitue = True
        else:
            if int(st[1]) == self.bar.updateTime.minute:
                newMinitue = False
            else:
                newMinitue = True
        # 如果是新1分钟，生成一个新k线变量，CBarData结构体中有OHLC,time等K线字段
        if newMinitue:
            self.bar = CBarData()
            self.bar.instrumentID = pDepthMarketData.InstrumentID
            self.bar.exchangeID = pDepthMarketData.ExchangeID
            self.bar.updateTime = time(int(st[0]), int(st[1]), 0, 0)
            self.bar.volume = 0
            self.bar.openInterest = pDepthMarketData.OpenInterest
            self.bar.openPrice = pDepthMarketData.LastPrice
            self.bar.highPrice = pDepthMarketData.LastPrice
            self.bar.lowPrice = pDepthMarketData.LastPrice
            self.bar.closePrice = pDepthMarketData.LastPrice
        else:
            # 如果不是新1分钟，将最新价与HL价相比然后更新，跟新C价
            self.bar.highPrice = max(self.bar.highPrice, pDepthMarketData.LastPrice)
            self.bar.lowPrice = min(self.bar.lowPrice, pDepthMarketData.LastPrice)
            self.bar.closePrice = pDepthMarketData.LastPrice
            self.bar.openInterest = pDepthMarketData.OpenInterest
        # 注意Volume字段是累计成交量，所以这个时间段内成交量为该值与上一时间段末成交量的差值
        if not self.lastVolume:
            self.bar.volume += max(pDepthMarketData.Volume - self.lastVolume, 0)
        self.lastVolume = pDepthMarketData.Volume
        # 打印实时k线数据
        print(f"bar:{self.bar.updateTime},O[{self.bar.openPrice}],H[{self.bar.highPrice}],L[{self.bar.lowPrice}],C[{self.bar.closePrice}]")

def main():
   mduserapi=mdapi.CThostFtdcMdApi_CreateFtdcMdApi() #第1步
   mduserspi=CFtdcMdSpi(mduserapi)  #第2步
   '''以下是生产环境'''
   #mduserapi.RegisterFront("tcp://180.168.146.187:10101")  #第3步
   '''以下是7*24小时环境'''
   mduserapi.RegisterFront("tcp://218.202.237.33:10112")
   mduserapi.RegisterSpi(mduserspi) #第4步
   mduserapi.Init()    #第5步，API正式启动，dll底层会自动去连上面注册的地址
   mduserapi.Join()    #join的目的是为了阻塞主线程，可以用sleep代替

if __name__ == '__main__':
    main()